package common.db;

import common.utils.LinqUtil;
import common.utils.StringUtil;
import lombok.var;

import java.sql.Timestamp;
import java.util.Date;
import java.util.LinkedList;

public class CodeFactory {

    public static void setSqlType(ColumnInfo column) {
        if (StringUtil.equals(column.javaType,
                StringUtil.subFromEnd(int.class.getTypeName(), ".", false),
                StringUtil.subFromEnd(Integer.class.getTypeName(), ".", false))) {
            column.sqlType = "INTEGER";
            return;
        }
        if (StringUtil.equals(column.javaType,
                StringUtil.subFromEnd(long.class.getTypeName(), ".", false),
                StringUtil.subFromEnd(Long.class.getTypeName(), ".", false))) {
            column.sqlType = "BIGINT";
            return;
        }
        if (StringUtil.equals(column.javaType,
                StringUtil.subFromEnd(Date.class.getTypeName(), ".", false),
                StringUtil.subFromEnd(Timestamp.class.getTypeName(), ".", false))) {
            column.sqlType = "TIMESTAMP";
            return;
        }
        if (StringUtil.equals(column.javaType,
                StringUtil.subFromEnd(boolean.class.getTypeName(), ".", false),
                StringUtil.subFromEnd(Boolean.class.getTypeName(), ".", false))) {
            column.sqlType = "BIT";
            return;
        }
        column.sqlType = "VARCHAR";
        column.size = 50;
    }

    public static TableInfo getTableInfo(String entityCode) {
        var table = new TableInfo();
        table.comment = StringUtil.getBetween(entityCode,
                "@ApiModel(description = \"", "\")", false);
        table.name = StringUtil.getBetween(entityCode,
                "public class ", " {", false);
        table.name = StringUtil.getUnderline(table.name);
        var columns = new LinkedList<ColumnInfo>();
        var fieldStrings = StringUtil.getAllBetweens(entityCode,
                "private", ";", true);
        for (var fieldString : fieldStrings) {
            var column = new ColumnInfo();
            column.javaType = StringUtil.getBetween(fieldString,
                    "private ", " ", false);
            column.name = StringUtil.getBetween(fieldString,
                    " ", ";", false);
            columns.add(column);
        }
        fieldStrings = StringUtil.getAllBetweens(entityCode,
                "@ApiModelProperty", ";", true);
        for (var fieldString : fieldStrings) {
            var comment = StringUtil.getBetween(fieldString,
                    "@ApiModelProperty(\"", "\")", false);
            var name = StringUtil.getBetween(fieldString,
                    " ", ";", false);
            var column = LinqUtil.filterOne(columns, m -> m.name.equals(name));
            if (column != null)
                column.comment = comment;
        }
        fieldStrings = StringUtil.getAllBetweens(entityCode,
                "@TableField", ";", true);
        for (var fieldString : fieldStrings) {
            var name = StringUtil.getBetween(fieldString,
                    " ", ";", false);
            LinqUtil.removeBy(columns, ColumnInfo::getName, name);
        }
        for (var column : columns) {
            column.name = StringUtil.toUnderline(column.name);
            setSqlType(column);
        }
        table.columns = columns;
        return table;
    }

    public static String getCommentSql(TableInfo table) {
        var createSql = new StringBuilder();
        if (StringUtil.isNotEmpty(table.comment))
            createSql.append("COMMENT ON TABLE " + table.name +
                    " IS '" + table.comment + "';\n");
        for (var column : table.columns)
            if (StringUtil.isNotEmpty(column.comment))
                createSql.append("COMMENT ON COLUMN " + table.name + "." +
                        StringUtil.addDoubleQuotes(column.name) +
                        " IS '" + column.comment + "';\n");
        return createSql.toString();
    }

    public static String getCreateSql(TableInfo table) {
        var createSql = new StringBuilder();
        createSql.append("CREATE TABLE " + table.name);
        createSql.append("\n(\r\n");
        for (var column : table.columns)
            createSql.append(StringUtil.addDoubleQuotes(column.name) + " " +
                    column.sqlType + column.getSize() + ",\n");
        createSql.append("CONSTRAINT primary_" + table.name + " CLUSTER PRIMARY KEY(id)");
        createSql.append("\n)\n");
        createSql.append("STORAGE(ON MAIN, CLUSTERBTR);\n");
        createSql.append(getCommentSql(table));
        return createSql.toString();
    }

    public static String getAlterSql(TableInfo table, TableInfo existTable) {
        var createSql = new StringBuilder();
        var removeColumns = LinqUtil.filter(existTable.columns, existColumn ->
        {
            var newColumn = LinqUtil.filterOne(table.columns, _newColumn ->
                    _newColumn.name.equals(existColumn.name));
            if (newColumn == null)
                return true;
            return !newColumn.sqlType.equals(existColumn.sqlType);
        });
        for (var column : removeColumns)
            createSql.append("ALTER TABLE " + table.name + " DROP " +
                    StringUtil.addDoubleQuotes(column.name) + ";\n");
        var addColumns = LinqUtil.filter(table.columns, newColumn ->
        {
            var existColumn = LinqUtil.filterOne(existTable.columns, _existColumn ->
                    newColumn.name.equals(_existColumn.name));
            if (existColumn == null)
                return true;
            return !newColumn.sqlType.equals(existColumn.sqlType);
        });
        for (var column : addColumns)
            createSql.append("ALTER TABLE " + table.name + " ADD " +
                    StringUtil.addDoubleQuotes(column.name) + " " +
                    column.sqlType + column.getSize() + ";\n");
        createSql.append(getCommentSql(table));
        return createSql.toString();
    }
}
